﻿using System.Collections.Concurrent;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualRoutes.DataSourceRoutes.RouteRuleEngine;
using ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions;
using ShardingCore.TableCreator;

namespace ApplicationFrame.EntityFrameworkCore.Core
{
    /// <summary>
    /// 通用分表路由（基于AbstractShardingOperatorVirtualTableRoute）
    /// </summary>
    /// <typeparam name="TEntity">实体类型</typeparam>
    /// <typeparam name="TKey">分表键类型</typeparam>
    public class GenericShardingRoute<TEntity, TKey> : AbstractShardingOperatorVirtualTableRoute<TEntity, TKey>
        where TEntity : class
    {
        private readonly IShardingStrategy<TEntity, TKey> _strategy;
        private readonly IVirtualDataSource _virtualDataSource;
        private readonly IShardingTableCreator _shardingTableCreator;
        private readonly object _lock = new object();
        private readonly ConcurrentDictionary<string, string> _tails = new ConcurrentDictionary<string, string>();

        // 注入策略工厂，初始化分表策略
        public GenericShardingRoute(
            ShardingStrategyFactory strategyFactory,
            IVirtualDataSource virtualDataSource,
            IShardingTableCreator shardingTableCreator)
        {
            _strategy = strategyFactory.Create<TEntity, TKey>();
            _virtualDataSource = virtualDataSource;
            _shardingTableCreator = shardingTableCreator;
        }

        /// <summary>
        /// 配置实体分表元数据（绑定分表键）
        /// </summary>
        public override void Configure(EntityMetadataTableBuilder<TEntity> builder)
        {
            _strategy.Configure(builder); // 委托给分表策略
            builder.TableSeparator("_");
        }

        /// <summary>
        /// 如何路由到具体表 shardingKeyValue:分表的值, 返回结果:如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表
        /// </summary>
        public override Func<string, bool> GetRouteToFilter(TKey shardingKey, ShardingOperatorEnum shardingOperator)
        {
            if (shardingKey == null)
                throw new ArgumentNullException(nameof(shardingKey), "分表键不能为空");
            return _strategy.GetRouteToFilter(shardingKey, shardingOperator);
        }

        public override string ShardingKeyToTail(object shardingKey)
        {
            var key = (TKey)Convert.ChangeType(shardingKey, typeof(TKey));
            return _strategy.GetTail(key);
        }

        public override List<string> GetTails()
        {
            return _tails.Keys.ToList();
        }

        public override TableRouteUnit RouteWithValue(DataSourceRouteResult dataSourceRouteResult, object shardingKey)
        {
            var tail = ShardingKeyToTail(shardingKey);
            if (!_tails.TryGetValue(tail, out _))
            {
                lock (_lock)
                {
                    if (!_tails.TryGetValue(tail, out _))
                    {
                        try
                        {
                            _shardingTableCreator.CreateTable<TEntity>(_virtualDataSource.DefaultDataSourceName, tail);
                        }
                        catch(Exception e)
                        {
                            Console.WriteLine($"建表失败：{e}");
                        }

                        _tails.TryAdd(tail, null);
                    }
                }
            }
            
            return base.RouteWithValue(dataSourceRouteResult, shardingKey);
        }
    }
}
